﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson7
{
    #region 继承
    // 概念
    //   类A继承类B， 类A会继承类B的所有成员，A类将拥有B类的所有特征和行为
    //  被继承的类叫 父类、基类、超类、
    //  继承的类：称为 子类、派生类
    //  子类也可以有自己的特征和行为
    // 1、单根性 子类只有有一个父类
    // 2、传递性，子类可以继承父类的

    // 继承语法
    // class 类名 ： 被继承的类名
    class Teacher
    {
        public string name;
        public string description;
        public int phone;

        public void Introduce()
        {
            Console.WriteLine(name);
        }
    }

    class TeachingTeacher : Teacher    // 方式跟python一样
    {
        //public new string name;     // 覆盖父类的同名变量，不加new也是覆盖的，但会报提示，不过不建议这样用
        public string subject;

        public void Say()
        {
            Console.WriteLine(subject);
        }
    }
    class EnglishTeacher : TeachingTeacher
    {
        public string englishName;

        public void SpeakEnglish()
        {
            Console.WriteLine(englishName);
        }
    }

    // 访问修饰符的影响
    //   public  - 公共    内外部访问
    //   private - 私有的   内部访问
    //   protected - 保护   内部和子类访问

    // internal - 内部的 只有在同一个程序集的文件中，内部类型或者时成员才可以访问

    // c#中允许子类存在和父类同名的成员，但极不建议使用

    #endregion

    #region 里氏替换原则
    // 概念： 任何父类出现的地方，子类都可以替代
    // 重点： 语法表现 - 父类容器装子类对象，因为子类对象包含了父类所有的内容
    //        方便进行对象存储和管理
    class GameObject
    {
        public void attack()
        {
            Console.WriteLine("攻击");
        }
    }

    class Player:GameObject
    {
        public void PlayerAtk()
        {
            Console.WriteLine("玩家攻击");
        }
    }
    class Monster : GameObject
    {
        public void MonsterAtk()
        {
            Console.WriteLine("怪物攻击");
        }
    }
    class Boss : GameObject
    {
        public void BossAtk()
        {
            Console.WriteLine("Boss攻击");
        }
    }

    #endregion

    #region 继承中的构造函数
    // 基本概念
    //   当声明一个子类对象时
    //   先执行父类的构造函数
    //   再执行子类的构造函数
    //  注意：
    //    1、父类的无参构造 很重要
    //    2、子类可以通过base关键字 代表父类 调用父类构造

    class Animal
    {
        public Animal()
        {
            Console.WriteLine("Animal构造函数");
        }
    }
    class Cat : Animal      
    {
        public Cat()         
        {
            Console.WriteLine("Cat构造函数");
        }
    }
    class CalicoCat: Animal
    {
        public CalicoCat()
        {
            Console.WriteLine("花猫的构造函数");
        }
    }

    class Father
    {
        //public Father()
        //{
        //    Console.WriteLine("超类无参构造函数");
        //}
        public Father(int i)
        {
            Console.WriteLine("超类有参构造函数");
        }
    }
    // base是指向父类的
    class Son : Father                  // 把父类的无参构造顶掉后，这里会报错，因为要先执行父类的无参构造
    {
        public Son(int i) : base(i)   // 解决报错的方式是 使用base先调用父类的有参构造函数，子类也不能使用无参构造了
        {
            Console.WriteLine("子类构造函数");
        }

        public Son(int i, string name):this(i)
        {
            Console.WriteLine("两个参数");
        }
    }
    #endregion

    #region 万物之父
    // 所有对象都有一个父对象
    // 关键字：object
    // 概念：
    //      object是所有类型的基类，它是一个类（引用类型）
    //   作用：1、可以利用里氏替换原则，用object容器装所有对象 2、可以用来表示不确定类型，作为函数参数类型
    class Mother
    {

    }
    class Daughter : Mother
    {
        public void Say()
        {

        }
    }
    #endregion

    #region 密封类
    // 密封类 是使用 sealed密封关键字修饰的类
    // 作用：让类无法再被继承
    //sealed class Previous
    class Previous
    {

    }
    sealed class Next : Previous  // 当Previous类前面加了sealed, 这里会报错
    {

    }

    // 密封类作用：在面向对象程序的设计中，密封类主要作用就是不允许最底层子类被继承
    //   可以保证程序的规范性、安全性
    
    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            // 面向对象的继承

            TeachingTeacher teachingTeacher = new TeachingTeacher();
            teachingTeacher.name = "李老师";
            teachingTeacher.phone = 123456789;
            teachingTeacher.Introduce();

            teachingTeacher.subject = "语文";
            teachingTeacher.Say();
            

            EnglishTeacher englishTea = new EnglishTeacher();
            englishTea.name = "王老师";
            englishTea.englishName = "Mr.wang";
            englishTea.SpeakEnglish();

            // 里氏替换原则  用父类容器 装载子类对象
            GameObject player = new Player();
            GameObject monster = new Monster();
            GameObject boss = new Boss();

            GameObject[] objects = { player, monster, boss };

            // is和as
            // is关键字：判断一个对象是否是指定类对象
            //   返回值：bool  是为true  不是为false
            // as 关键字：将一个对象转换为指定类对象
            //   返回值：指定类型对象
            //  成功返回指定类型对象，失败返回null
            // 语法
            //  类对象 is 类名   该语句块 会有一个bool返回值  true和false
            //  类对象 as 类名   该语句块 会有一个对象返回值  对象和null

            if(player is Player)
            {
                Player player1 = player as Player;
                player1.PlayerAtk();
                //(player as Player).PlayerAtk();
            }

            for(int i = 0; i < objects.Length; i++)
            {
                if(objects[i] is Player)
                {
                    (objects[i] as Player).PlayerAtk();
                }
            }

            Animal cat = new CalicoCat();

            // 万物之父的使用，使用父类型装子类对象
            Mother m = new Daughter();
            if(m is Daughter)
            {
                (m as Daughter).Say();
            }
            // 引用类型
            object o = new Daughter();
            if(o is Daughter)
            {
                (o as Daughter).Say();
            }
            // 值类型
            object o2 = 1f;
            // 用强转
            float f1 = (float)o2;
            // 特殊的string类型
            object os = "123456";
            //string str = os.ToString();
            string str = os as string;

            object arr = new int[2];
            int[] arrs = arr as int[];  // 可以用强转，但要是引用类型，推荐用 as

            #region 装箱拆箱
            // 用object存值类型 称为 装箱
            // 把object转为值类型 称为 拆箱

            // 装箱 把值类型 用 引用类型存储  栈内存会迁移到堆内存中
            // 拆箱 把引用类型存储的值类型取出来  堆内存会迁移到栈内存中

            // 好处：不确定类型时可以方便参数的存储和传递
            // 坏处：存在内存迁移，增加性能消耗

            #endregion
        }
    }
}
